﻿

''' <summary>
''' Base32Hex codec according to RFC4648.
''' </summary>
Public Module Base32HexCodec

#Region "Private Fields"
    'Base32Hex Alphabet Array:
    Private _Base32HexAlphabet As Char() = "0123456789ABCDEFGHIJKLMNOPQRSTUV".ToCharArray
    'Encodes 5 Bytes to 8 Base32Hex characters.
    Private Function _Encode5BytesTo8Chars(ByVal b0 As Byte, ByVal b1 As Byte, ByVal b2 As Byte, ByVal b3 As Byte, ByVal b4 As Byte) As String
        Return (_Base32HexAlphabet((b0 >> 3)) & _Base32HexAlphabet((((b0 << 5) >> 3) Or (b1 >> 6))) & _Base32HexAlphabet(((b1 << 2) >> 3)) & _Base32HexAlphabet((((b1 << 7) >> 3) Or (b2 >> 4))) & _
                _Base32HexAlphabet((((b2 << 4) >> 3) Or (b3 >> 7))) & _Base32HexAlphabet(((b3 << 1) >> 3)) & _Base32HexAlphabet((((b3 << 6) >> 3) Or (b4 >> 5))) & _Base32HexAlphabet(((b4 << 3) >> 3)))
    End Function
    'Encodes 4 Bytes to 8 Base32Hex characters, padded with 1 "="c.
    Private Function _Encode4BytesTo8Chars(ByVal b0 As Byte, ByVal b1 As Byte, ByVal b2 As Byte, ByVal b3 As Byte) As String
        Return (_Base32HexAlphabet((b0 >> 3)) & _Base32HexAlphabet((((b0 << 5) >> 3) Or (b1 >> 6))) & _Base32HexAlphabet(((b1 << 2) >> 3)) & _Base32HexAlphabet((((b1 << 7) >> 3) Or (b2 >> 4))) & _
                _Base32HexAlphabet((((b2 << 4) >> 3) Or (b3 >> 7))) & _Base32HexAlphabet(((b3 << 1) >> 3)) & _Base32HexAlphabet(((b3 << 6) >> 3)) & "="c)
    End Function
    'Encodes 3 Bytes to 8 Base32Hex characters, padded with 3 "="c.
    Private Function _Encode3BytesTo8Chars(ByVal b0 As Byte, ByVal b1 As Byte, ByVal b2 As Byte) As String
        Return (_Base32HexAlphabet((b0 >> 3)) & _Base32HexAlphabet((((b0 << 5) >> 3) Or (b1 >> 6))) & _Base32HexAlphabet(((b1 << 2) >> 3)) & _Base32HexAlphabet((((b1 << 7) >> 3) Or (b2 >> 4))) & _Base32HexAlphabet(((b2 << 4) >> 3)) & "="c & "="c & "="c)
    End Function
    'Encodes 2 Bytes to 8 Base32Hex characters, padded with 4 "="c.
    Private Function _Encode2BytesTo8Chars(ByVal b0 As Byte, ByVal b1 As Byte) As String
        Return (_Base32HexAlphabet((b0 >> 3)) & _Base32HexAlphabet((((b0 << 5) >> 3) Or (b1 >> 6))) & _Base32HexAlphabet(((b1 << 2) >> 3)) & _Base32HexAlphabet(((b1 << 7) >> 3)) & "="c & "="c & "="c & "="c)
    End Function
    'Encodes 1 Byte to 8 Base32Hex characters, padded with 6 "="c.
    Private Function _Encode1BytesTo8Chars(ByVal b0 As Byte) As String
        Return (_Base32HexAlphabet((b0 >> 3)) & _Base32HexAlphabet(((b0 << 5) >> 3)) & "="c & "="c & "="c & "="c & "="c & "="c)
    End Function
    'Decode 8 Base32Hex bytes to 5 bytes.
    Private Function _Decode8B32Bytes(ByVal b0 As Byte, ByVal b1 As Byte, ByVal b2 As Byte, ByVal b3 As Byte, ByVal b4 As Byte, ByVal b5 As Byte, ByVal b6 As Byte, ByVal b7 As Byte) As Byte()
        Return {((b0 << 3) Or (b1 >> 2)), ((b1 << 6) Or (b2 << 1) Or (b3 >> 4)), ((b3 << 4) Or (b4 >> 1)), (b4 << 7) Or ((b5 << 2) Or (b6 >> 3)), ((b6 << 5) Or (b7))}
    End Function
    'Decode 7 Base32Hex bytes to 4 bytes.
    Private Function _Decode7B32Bytes(ByVal b0 As Byte, ByVal b1 As Byte, ByVal b2 As Byte, ByVal b3 As Byte, ByVal b4 As Byte, ByVal b5 As Byte, ByVal b6 As Byte) As Byte()
        Return {((b0 << 3) Or (b1 >> 2)), ((b1 << 6) Or (b2 << 1) Or (b3 >> 4)), ((b3 << 4) Or (b4 >> 1)), (b4 << 7) Or ((b5 << 2) Or (b6 >> 3))}
    End Function
    'Decode 5 Base32Hex bytes to 3 bytes.
    Private Function _Decode5B32Bytes(ByVal b0 As Byte, ByVal b1 As Byte, ByVal b2 As Byte, ByVal b3 As Byte, ByVal b4 As Byte) As Byte()
        Return {((b0 << 3) Or (b1 >> 2)), ((b1 << 6) Or (b2 << 1) Or (b3 >> 4)), ((b3 << 4) Or (b4 >> 1))}
    End Function
    'Decode 4 Base32Hex bytes to 2 bytes.
    Private Function _Decode4B32Bytes(ByVal b0 As Byte, ByVal b1 As Byte, ByVal b2 As Byte, ByVal b3 As Byte) As Byte()
        Return {((b0 << 3) Or (b1 >> 2)), ((b1 << 6) Or (b2 << 1) Or (b3 >> 4))}
    End Function
    'Decode 2 Base32Hex bytes to 1 bytes.
    Private Function _Decode2B32Bytes(ByVal b0 As Byte, ByVal b1 As Byte) As Byte
        Return ((b0 << 3) Or (b1 >> 2))
    End Function
#End Region

#Region "Public Methods"
    'IsValid
    ''' <summary>
    ''' Checks if a given Base32Hex String is valid or not.
    ''' </summary>
    ''' <param name="b32HexString">the Base32Hex string to validate.</param>
    Public Function IsValid(ByVal b32HexString As String) As Boolean
        If b32HexString.IndexOf("=") < b32HexString.Length - 6 Then
            Return False
        Else
            For Each c As Char In b32HexString.ToCharArray
                If _Base32HexAlphabet.Contains(c) = False Then
                    Return False
                End If
            Next
            Return True
        End If
    End Function
    'Encode
    ''' <summary>
    ''' Gets the Base32Hex string representation of the given bytes array.
    ''' Return Nothing, if (sourceBytes) is Nothing or empty.
    ''' </summary>
    ''' <param name="sourceBytes">The bytes array that will be converted to Base32Hex string.</param>
    Public Function Encode(ByVal sourceBytes As Byte()) As String
        If sourceBytes IsNot Nothing AndAlso sourceBytes.Count > 0 Then
            Dim B32HexS As String = ""
            Dim count As Integer = sourceBytes.Count
            Dim remains As Integer = (count Mod 5)
            Dim groups As Integer = CInt(((count - remains) / 5))
            For g As Integer = 0 To groups - 1
                B32HexS &= _Encode5BytesTo8Chars(sourceBytes((5 * g) + 0), sourceBytes((5 * g) + 1), sourceBytes((5 * g) + 2), sourceBytes((5 * g) + 3), sourceBytes((5 * g) + 4))
            Next
            If remains = 4 Then
                B32HexS &= _Encode4BytesTo8Chars(sourceBytes((count - 4)), sourceBytes((count - 3)), sourceBytes((count - 2)), sourceBytes((count - 1)))
            ElseIf remains = 3 Then
                B32HexS &= _Encode3BytesTo8Chars(sourceBytes((count - 3)), sourceBytes((count - 2)), sourceBytes((count - 1)))
            ElseIf remains = 2 Then
                B32HexS &= _Encode2BytesTo8Chars(sourceBytes((count - 2)), sourceBytes((count - 1)))
            ElseIf remains = 1 Then
                B32HexS &= _Encode1BytesTo8Chars(sourceBytes((count - 1)))
            End If
            Return B32HexS
        Else
            Return Nothing
        End If
    End Function
    'Encode
    ''' <summary>
    ''' Gets the Base32Hex string representation of the given source string according to its source text encoding.
    ''' </summary>
    ''' <param name="sourceString">The source string that will be converted to Base32Hex string.</param>
    ''' <param name="sourceEncoding">The text encoding of the source string.</param>
    Public Function Encode(ByVal sourceString As String, ByVal sourceEncoding As Text.Encoding) As String
        Return Base32HexCodec.Encode(sourceEncoding.GetBytes(sourceString))
    End Function
    'Decode
    ''' <summary>
    ''' Gets the bytes array representing the given Base32Hex string.
    ''' </summary>
    ''' <param name="b32HexString">The Base32Hex string to be decoded.</param>
    Public Function Decode(ByVal b32HexString As String) As Byte()
        Dim tb32String As String = b32HexString.TrimEnd("="c)
        Dim b32Hexbytes As Byte() = (From c As Char In tb32String.ToCharArray Select CByte(Array.IndexOf(_Base32HexAlphabet, c))).ToArray
        Dim count As Integer = b32Hexbytes.Count
        Dim remains As Integer = (count Mod 8)
        Dim groups As Integer = CInt((count - remains) / 8)
        Dim result As New List(Of Byte)
        For g As Integer = 0 To groups - 1
            result.AddRange(_Decode8B32Bytes(b32Hexbytes(8 * g + 0), b32Hexbytes(8 * g + 1), b32Hexbytes(8 * g + 2), b32Hexbytes(8 * g + 3), b32Hexbytes(8 * g + 4), b32Hexbytes(8 * g + 5), b32Hexbytes(8 * g + 6), b32Hexbytes(8 * g + 7)))
        Next
        If remains = 7 Then
            result.AddRange(_Decode7B32Bytes(b32Hexbytes((count - 7)), b32Hexbytes((count - 6)), b32Hexbytes((count - 5)), b32Hexbytes((count - 4)), b32Hexbytes((count - 3)), b32Hexbytes((count - 2)), b32Hexbytes((count - 1))))
        ElseIf remains = 5 Then
            result.AddRange(_Decode5B32Bytes(b32Hexbytes((count - 5)), b32Hexbytes((count - 4)), b32Hexbytes((count - 3)), b32Hexbytes((count - 2)), b32Hexbytes((count - 1))))
        ElseIf remains = 4 Then
            result.AddRange(_Decode4B32Bytes(b32Hexbytes((count - 4)), b32Hexbytes((count - 3)), b32Hexbytes((count - 2)), b32Hexbytes((count - 1))))
        ElseIf remains = 2 Then
            result.Add(_Decode2B32Bytes(b32Hexbytes((count - 2)), b32Hexbytes((count - 1))))
        End If
        Return result.ToArray
    End Function
    'Decode
    ''' <summary>
    ''' Gets the string representing the given Base32Hex string.
    ''' </summary>
    ''' <param name="b32HexString">The Base32Hex string to be decoded.</param>
    ''' <param name="resultEncoding">The text encoding for the resulting string.</param>
    Public Function Decode(ByVal b32HexString As String, resultEncoding As Text.Encoding) As String
        Return resultEncoding.GetString(Base32HexCodec.Decode(b32HexString))
    End Function
#End Region

End Module